<template>
  <div>
    <el-card style="height: 80px">
      <el-form ref="form" :inline="true" class="form">
        <el-form-item label="角色名称">
          <el-input placeholder="请输入搜索职位关键字" v-model="keyword"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" :disabled="keyword.trim() ? false : true" @click="searchRole">
            搜索
          </el-button>
          <el-button @click="reset">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>

    <el-card>
      <el-button type="primary" icon="Plus" @click="addRole">
        添加角色
      </el-button>
      <el-table boder :data="roleList">
        <el-table-column type="index" label="#" align="center" width="80px"></el-table-column>
        <el-table-column label="id" align="center" prop="id"></el-table-column>
        <el-table-column label="角色名称" align="center" prop="roleName" show-overflow-tooltip></el-table-column>
        <el-table-column label="创建时间" align="center" prop="createTime" show-overflow-tooltip></el-table-column>
        <el-table-column label="更新时间" align="center" prop="updateTime" show-overflow-tooltip></el-table-column>
        <el-table-column label="操作" align="center" width="270px" fixed="right">
          <!-- row: 表示对应行的信息对象，已有的职位对象 -->
          <template #="{ row }">
            <el-button type="primary" icon="User" size="small" @click="assignPermission(row)">
              分配权限
            </el-button>
            <el-button type="primary" icon="Edit" size="small" @click="updateRole(row)">
              编辑
            </el-button>
            <el-popconfirm :title="`你确定要删除角色${row.roleName}吗?`" @confirm="deleteRole(row.id)" width="250px">
              <template #reference>
                <el-button type="primary" icon="Delete" size="small">
                  删除
                </el-button>
              </template>
            </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>

      <!-- 分页器 -->
      <el-pagination v-model:current-page="pageNo" v-model:page-size="pageSize" :page-sizes="[3, 5, 7, 9]"
        :background="true" layout="prev, pager, next, jumper, ->, sizes, total" :total="total" />

      <!-- 对话框：新增或者更新角色信息 -->
      <el-dialog v-model="dialogFormVisible" :title="roleParams.id ? '修改角色' : '新增角色'">
        <el-form ref="addOrUpdateFormRef" :model="roleParams" :rules="rules">
          <el-form-item label="角色名称" prop="roleName">
            <el-input placeholder="请填写角色名称" v-model="roleParams.roleName" />
          </el-form-item>
        </el-form>
        <template #footer>
          <el-button @click="dialogFormVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmRole">确定</el-button>
        </template>
      </el-dialog>

      <!-- 抽屉：给角色分配菜单权限与按钮权限 -->
      <el-drawer v-model="drawer" direction="rtl">
        <template #header>
          <h4>分配菜单与按钮的权限</h4>
        </template>
        <template #default>
          <!-- 树形控件 -->
          <!-- 
            show-checkbox  节点是否可被选择，默认为false,即节点前是否有复选框 
            node-key  每个树节点用来作为唯一标识的属性，整棵树应该是唯一的
            default-expanded-keys	默认展开的节点的 key 的数组
            default-checked-keys	默认勾选的节点的 key 的数组
            default-expand-all	是否默认展开所有节点，默认为false
            props	配置选项，具体看下表
              label	指定节点标签为节点对象的某个属性值,即决定树形控件展示的数据用哪个字段
              children	指定子树为节点对象的某个属性值
          -->
          <el-tree ref="tree" :data="menuList" show-checkbox node-key="id" default-expand-all :props="defaultProps"
            :default-checked-keys="checkedKeyList" />
        </template>
        <template #footer>
          <div style="flex: auto">
            <el-button @click="drawer = false">取消</el-button>
            <el-button type="primary" @click="savePermisstion">确定</el-button>
          </div>
        </template>
      </el-drawer>
    </el-card>
  </div>
</template>
<script setup lang="ts">
import {
  reqAddOrUpdateRole,
  reqAllRoleList,
  reqAllMenuList,
  reqAssignPermisstion,
  reqDeleteRole,
} from '@/api/acl/role'
import { ref, onMounted, watch, reactive } from 'vue'
import type {
  Role,
  RoleList,
  RoleResponseData,
  MenuList,
  MenuResponseData,
} from '@/api/acl/role/type'
// 引入小仓库
import useLayoutSetingStore from '@/store/modules/setting'
import { ElMessage } from 'element-plus/lib/components/index.js'

// 当前页码
let pageNo = ref<number>(1)
// 每页几条数据
let pageSize = ref<number>(3)
// 数据总条数
let total = ref<number>(0)
// 存储搜索的角色名
let keyword = ref<string>('')
// 存储角色列表数据
let roleList = ref<RoleList>([])
// 获取小仓库的变量
let useStore = useLayoutSetingStore()
// 控制展示或者关闭对框框
let dialogFormVisible = ref<boolean>(false)
// 存储搜集到的角色的数据
let roleParams = reactive<Role>({ id: '', roleName: '' })
// 获取el-form表单实例对象,后续进行表单验证
let addOrUpdateFormRef = ref()
// 控制抽屉的显示与隐藏
let drawer = ref<boolean>(false)

// 树形控件配置
const defaultProps = {
  children: 'children', // 指定子树为节点对象的某个属性值
  label: 'name', // 指定节点标签为节点对象的某个属性值
}
// 树形控件要展示的数据
// const data = [
//   {
//     id: 1,
//     label: 'Level one 1',
//     children: [
//       {
//         id: 4,
//         label: 'Level two 1-1',
//         children: [
//           {
//             id: 9,
//             label: 'Level three 1-1-1',
//           },
//           {
//             id: 10,
//             label: 'Level three 1-1-2',
//           },
//         ],
//       },
//     ],
//   },
//   {
//     id: 2,
//     label: 'Level one 2',
//     children: [
//       {
//         id: 5,
//         label: 'Level two 2-1',
//       },
//       {
//         id: 6,
//         label: 'Level two 2-2',
//       },
//     ],
//   },
//   {
//     id: 3,
//     label: 'Level one 3',
//     children: [
//       {
//         id: 7,
//         label: 'Level two 3-1',
//       },
//       {
//         id: 8,
//         label: 'Level two 3-2',
//       },
//     ],
//   },
// ]
// 定义数组存储用户权限的数据
let menuList = ref<MenuList>([])
// 存储勾选的的菜单的id（四级）
let checkedKeyList = ref<number[]>([])
// 树形控件
let tree = ref<any>()

// 获取全部角色, 当用户不指定从哪一页开始展示数据时，默认从当前页；当用指定时pageNum时，要从用户指定的页面开始
const getRole = async (pageNum = pageNo.value) => {
  pageNo.value = pageNum
  let result: RoleResponseData = await reqAllRoleList(
    pageNo.value,
    pageSize.value,
    keyword.value,
  )
  if (result.code == 200) {
    // 存储数据总数
    total.value = result.data.total
    // 存储全部角色数据
    roleList.value = result.data.records
  }
}

// 组件挂载上之后请求获取角色
onMounted(() => {
  // 由于我们pageNo的默认值为1，所以当组件加载完成时，请求角色数据并从第一页开始展示
  getRole()
})

// 当前页发生变化时，需要再次发生请求用户信息，返回指定页的数据
watch(pageNo, () => {
  getRole()
})

// 当改变每页要显示的数据的个数时，要从第一页开始展示
watch(pageSize, () => {
  getRole(1)
})

// 搜索角色按钮回调
const searchRole = () => {
  getRole()
  // 展示完数据，清空keyword
  keyword.value = ''
}

// 重置按钮回调(销毁组件重建)
const reset = () => {
  useStore.isRefresh = !useStore.isRefresh
}

// 添加角色按钮回调
const addRole = () => {
  // 对话框显示出来
  dialogFormVisible.value = true
  // 新增之前先要清空之前的数据
  Object.assign(roleParams, { id: '', roleName: '' })
  // 在操作角色之前需要清楚之前的验证信息(第一次操作由于还没有弹出对话框，因为对话框中form并不存在，所以这里要加个判断)
  addOrUpdateFormRef.value?.clearValidate('roleName')
}

// 编辑按钮的回调
const updateRole = (row: Role) => {
  // 对话框显示出来
  dialogFormVisible.value = true
  // 把当前行的数据赋值给roleParams
  Object.assign(roleParams, row)
  // 在操作角色之前需要清楚之前的验证信息
  addOrUpdateFormRef.value?.clearValidate('roleName')
}

// 对框框确定按钮回调：新增角色或者修改角色
const confirmRole = async () => {
  // 表单验证,验证通过之后才能发请求,用try catch的方式为了避免console底层代码的报提醒：错误未处理
  try {
    await addOrUpdateFormRef.value.validate()
  } catch (error) {
    console.log('角色名称非法:', error)
    return
  }
  // 关闭对话框
  dialogFormVisible.value = false
  let result = await reqAddOrUpdateRole(roleParams)
  if (result.code == 200) {
    ElMessage({
      type: 'success',
      message: roleParams.id ? '修改角色成功' : '新增角色成功',
    })
    // 重新获取角色数据，更新留在当前页，添加留在第一页
    getRole(roleParams.id ? pageNo.value : 1)
  } else {
    ElMessage({
      type: 'error',
      message: roleParams.id ? '修改角色失败' : '新增角色失败',
    })
  }
}

const validateRoleName = (_: any, value: any, callback: any) => {
  if (value.trim().length >= 3 && value.trim().length < 20) {
    callback()
  } else {
    callback(new Error('角色名称为3到20位字符'))
  }
}

// 新增或更新角色的表单校验
const rules = {
  roleName: [{ required: true, trigger: 'blur', validator: validateRoleName }],
}

// 给角色分配权限按钮的回调，主要是展示用户的权限以及已经勾选的权限
const assignPermission = async (row: Role) => {
  // 清空选中的值
  checkedKeyList.value = []
  // 显示抽屉
  drawer.value = true
  // 收集当前要分配权限的角色的数据(因为下面的保存时会用到)
  Object.assign(roleParams, row)
  // 根据角色获取权限的数据
  let result: MenuResponseData = await reqAllMenuList(roleParams.id as number)
  if (result.code == 200) {
    menuList.value = result.data
    // 获取最大的层级，这里获取的结果为levelData = {maxLevel: 4}
    let levelData: any = filterSelectMaxLevel(menuList.value, { maxLevel: 0 })
    // 获取选中的最大层级的id数组(这里我们可以以参数形式把上面获取的最大层级传进去),把复制给checkedKeyList，这样角色已有的权限就会勾选上
    checkedKeyList.value = filterSelectList(
      menuList.value,
      [],
      levelData.maxLevel,
    )
  }
}

// 递归获取勾选的最大层的id数组
const filterSelectList = (allData: any, initArr: any, maxLevel: number) => {
  allData.forEach((item: any) => {
    if (item.select && item.level == maxLevel) {
      initArr.push(item.id)
    }
    if (item.children && item.children.length > 0) {
      filterSelectList(item.children, initArr, maxLevel)
    }
  })
  return initArr
}

// 递归获取勾选的最大的层级数，用对象的形式为了避免使用全局变量
const filterSelectMaxLevel = (allData: any, levelData: any) => {
  allData.forEach((item: any) => {
    if (item.select && item.level > levelData.maxLevel) {
      levelData.maxLevel = item.level
    }
    if (item.children && item.children.length > 0) {
      filterSelectMaxLevel(item.children, levelData)
    }
  })
  return levelData
}

// 抽屉确认按钮的回调，保存更新角色的权限
const savePermisstion = async () => {
  // 请求给角色分配权限接口
  const roleId = roleParams.id as number
  // 选中节点的ID
  let arr = tree.value.getCheckedKeys()
  // 半选的ID
  let arr1 = tree.value.getHalfCheckedKeys()
  const permissionId = arr.concat(arr1)
  console.log(permissionId)
  // 下发权限
  let result: any = await reqAssignPermisstion(roleId, permissionId)
  if (result.code == 200) {
    // 关闭抽屉
    drawer.value = false
    // 提示信息
    ElMessage({ type: 'success', message: '分配权限成功' })
    // 页面刷新，刚好由于当前用户所属角色为当前分配的权限，权限已经变化了，需要更新，而这里我们为了省事不对当前用户是否属性当前角色。直接刷新页面
    window.location.reload()
  }
}

// 删除角色
const deleteRole = async (roleId: number) => {
  let result = await reqDeleteRole(roleId)
  if (result.code == 200) {
    // 提示信息
    ElMessage({ type: 'success', message: '删除角色成功' })
    // 再次获取角色信息,如果当前页数据总数大于1，删除后任然留在当前页；否则要跳到上一页
    getRole(roleList.value.length > 1 ? pageNo.value : pageNo.value - 1)
  } else {
    ElMessage({ type: 'error', message: '删除角色失败' })
  }
}
</script>
<script lang="ts">
export default {
  name: 'AclRole',
}
</script>
<style scoped>
.form {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>
